/*********************************************************************************
 *
 *        SolarDrawSpin.java
 *
 *    User spinning controls for solar system drawer
 *
 *********************************************************************************
 *    Copyright 2003 Brown University -- Steven P. Reiss
 *********************************************************************************
 *  Copyright 2003, Brown University, Providence, RI.
 *
 *              All Rights Reserved
 *
 *  Permission to use, copy, modify, and distribute this software and its
 *  documentation for any purpose other than its incorporation into a
 *  commercial product is hereby granted without fee, provided that the
 *  above copyright notice appear in all copies and that both that
 *  copyright notice and this permission notice appear in supporting
 *  documentation, and that the name of Brown University not be used in
 *  advertising or publicity pertaining to distribution of the software
 *  without specific, written prior permission.
 *
 *  BROWN UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 *  SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 *  FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL BROWN UNIVERSITY
 *  BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
 *  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 *  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 *  ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 *  OF THIS SOFTWARE.
 *
 ********************************************************************************/
/* RCS: $Header$ */
/*********************************************************************************
 *
 * $Log$
 *
 ********************************************************************************/
package solardraw;
import java.awt.Component;
import java.awt.event.*;
class SolarDrawSpin implements MouseListener, MouseMotionListener, KeyListener, MouseWheelListener {
    /********************************************************************************/
    /*                                        */
    /* Private Storage */
    /*                                        */
    /********************************************************************************/
    enum SpinType {
        NONE, ROTATE, MOVE, ZOOM
    };
    private SolarDrawCamera the_camera;
    private SpinType spin_type;
    private int spin_width;
    private int spin_height;
    private double spin_yz;
    private double spin_zx;
    private final static double PAN_SCALE = 2.0;
    private final static double ZOOM_SCALE = 10.0;
    private SolarDraw.Control control;
    /********************************************************************************/
    /*                                        */
    /* Constructors */
    /*                                        */
    /********************************************************************************/
    SolarDrawSpin(Component comp, SolarDrawCamera cam, SolarDraw.Control c) {
        the_camera = cam;
        control = c;
        spin_type = SpinType.NONE;
        comp.addMouseListener(this);
        comp.addMouseMotionListener(this);
        comp.addKeyListener(this);
        comp.addMouseWheelListener(this);
    }
    /********************************************************************************/
    /*                                        */
    /* Event handling methods */
    /*                                        */
    /********************************************************************************/
    public void mouseClicked(MouseEvent e) {
    }
    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
    }
    public void mousePressed(MouseEvent e) {
        if (spin_type != SpinType.NONE) {
            handleSpinEvent(e);
        } else if (e.getButton() == MouseEvent.BUTTON3) {
            startSpinEvent(e);
        }
    }
    public void mouseReleased(MouseEvent e) {
        if (spin_type != SpinType.NONE) {
            if (e.getButton() == MouseEvent.BUTTON3) {
                spin_type = SpinType.NONE;
            }
        }
    }
    public void mouseDragged(MouseEvent e) {
        if (spin_type != SpinType.NONE) {
            handleSpinEvent(e);
        }
    }
    public void mouseMoved(MouseEvent e) {
        if (spin_type != SpinType.NONE) {
            handleSpinEvent(e);
        }
    }
    public void keyPressed(KeyEvent e) {
        if (spin_type == SpinType.NONE) {
            handleKey(e);
        }
    }
    public void keyReleased(KeyEvent e) {
    }
    public void keyTyped(KeyEvent e) {
        if (spin_type == SpinType.NONE) {
            handleKey(e);
        }
    }
    /********************************************************************************/
    /*                                        */
    /* Handle spin events */
    /*                                        */
    /********************************************************************************/
    private void startSpinEvent(MouseEvent me) {
        spin_width = the_camera.getWindowWidth();
        spin_height = the_camera.getWindowHeight();
        if (me.isShiftDown()) {
            spin_type = SpinType.MOVE;
            spin_yz = me.getX();
            spin_zx = me.getY();
        } else if (me.isControlDown()) {
            spin_type = SpinType.ZOOM;
            spin_yz = me.getX();
            spin_zx = me.getY();
        } else {
            spin_type = SpinType.ROTATE;
            spinAngles(me.getX(), me.getY());
        }
    }
    private void handleSpinEvent(MouseEvent me) {
        double oldyz = spin_yz;
        double oldzx = spin_zx;
        switch (spin_type) {
            case NONE:
                return;
            case ROTATE:
                spinAngles(me.getX(), me.getY());
                if (spin_yz != oldyz || spin_zx != oldzx) {
                    doSpin(spin_yz - oldyz, spin_zx - oldzx);
                }
                break;
            case MOVE:
                spin_yz = me.getX();
                spin_zx = me.getY();
                if (spin_yz != oldyz) {
                    the_camera.panLeftRight(-PAN_SCALE * (oldyz - spin_yz)
                            / spin_width);
                }
                if (spin_zx != oldzx) {
                    the_camera.panUpDown(PAN_SCALE * (oldzx - spin_zx)
                            / spin_height);
                }
                break;
            case ZOOM:
                spin_yz = me.getX();
                spin_zx = me.getY();
                if (spin_yz != oldyz) {
                    the_camera.zoomInOut(ZOOM_SCALE * (oldyz - spin_yz)
                            / spin_width);
                }
                if (spin_zx != oldzx) {
                    the_camera.zoomInOut(-ZOOM_SCALE * (oldzx - spin_zx)
                            / spin_height);
                }
                break;
        }
    }
    /********************************************************************************/
    /*                                        */
    /* Virtual sphere methods */
    /*                                        */
    /********************************************************************************/
    private void spinAngles(int ix, int iy) {
        double x, y, z;
        x = (ix - spin_width / 2);
        x /= spin_width;
        y = (iy - spin_height / 2);
        y /= spin_height;
        z = 1 - x * x - y * y;
        if (z <= 0) {
            return;
        }
        z = Math.sqrt(z);
        spin_yz = Math.atan2(y, z);
        spin_zx = Math.atan2(z, x);
    }
    private void doSpin(double ud, double lr) {
        if (ud != 0) {
            the_camera.rotateUpDown(-ud * 180.0 / Math.PI);
        }
        if (lr != 0) {
            the_camera.rotateLeftRight(lr * 180.0 / Math.PI);
        }
    }
    /********************************************************************************/
    /*                                        */
    /* Key handlers */
    /*                                        */
    /********************************************************************************/
    private void handleKey(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_HOME:
                the_camera.resetSettings();
                break;
            case KeyEvent.VK_F12:
                the_camera.toggleCameraType();
                break;
            case KeyEvent.VK_UP:
            case KeyEvent.VK_KP_UP:
                the_camera.rotateUpDown(30.0);
                break;
            case KeyEvent.VK_DOWN:
            case KeyEvent.VK_KP_DOWN:
                the_camera.rotateUpDown(-30.0);
                break;
            case KeyEvent.VK_LEFT:
            case KeyEvent.VK_KP_LEFT:
                the_camera.rotateLeftRight(30.0);
                break;
            case KeyEvent.VK_RIGHT:
            case KeyEvent.VK_KP_RIGHT:
                the_camera.rotateLeftRight(-30.0);
                break;
            case KeyEvent.VK_PAGE_UP:
                the_camera.zoomInOut(-2);
                break;
            case KeyEvent.VK_PAGE_DOWN:
                the_camera.zoomInOut(2);
                break;
            case KeyEvent.VK_S:
                control.saveCalled();
                break;
            case KeyEvent.VK_Q:
                System.exit(0);
                break;
        }
    }
    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {
        the_camera.zoomInOut(e.getWheelRotation() > 0 ? 2 : -2);
    }
} // end of class SolarDrawSpin
/* end of SolarDrawSpin.java */
